# HTTP获取角度并控制舵机
# 每0.5秒从API获取角度数据，并根据返回的角度控制MG90S舵机

import requests
import json
from maix import pwm, time, sys

# 舵机控制参数定义
SERVO_PERIOD = 50     # 舵机控制频率50Hz，对应20ms周期
SERVO_MIN_DUTY = 2.5  # 最小占空比2.5%，对应0.5ms脉宽，控制舵机转到0度
SERVO_MAX_DUTY = 12.5 # 最大占空比12.5%，对应2.5ms脉宽，控制舵机转到180度

# API配置
API_URL = "https://api.wenfree.cn/MaixCAM_index/get_angle"
REQUEST_INTERVAL = 0.5  # 请求间隔0.5秒

# 根据设备ID获取对应的引脚和PWM编号
device_id = sys.device_id()
if device_id == "maixcam2":
    pin_name = "IO0_A31"  # MaixCAM2使用IO0_A31引脚
    pwm_id = 7           # PWM通道7
else:
    pin_name = "A19"     # 其他设备使用A19引脚
    pwm_id = 7           # PWM通道7

print(f"设备信息: {device_id['name']} ({device_id['id']})")
print(f"使用引脚: {pin_name}, PWM通道: {pwm_id}")

def angle_to_duty(angle_degrees):
    """
    将角度（0-180度）转换为PWM占空比
    
    参数:
        angle_degrees: 角度值，0-180度
    
    返回:
        对应的PWM占空比（2.5%-12.5%）
    
    原理:
        - 0度   -> 2.5%占空比  -> 0.5ms脉宽
        - 90度  -> 7.5%占空比  -> 1.5ms脉宽  
        - 180度 -> 12.5%占空比 -> 2.5ms脉宽
    """
    # 限制角度范围在0-180度之间
    if angle_degrees < 0:
        angle_degrees = 0
    elif angle_degrees > 180:
        angle_degrees = 180
    
    # 将角度转换为占空比
    percent = angle_degrees / 180.0 * 100.0
    return (SERVO_MAX_DUTY - SERVO_MIN_DUTY) * percent / 100.0 + SERVO_MIN_DUTY

def get_angle_from_api():
    """
    从API获取角度数据
    
    返回:
        成功: 角度值（float）
        失败: None
    """
    try:
        # 发送HTTP GET请求
        response = requests.get(API_URL, timeout=5)
        
        # 检查HTTP状态码
        if response.status_code == 200:
            # 解析JSON响应
            data = response.json()
            
            # 检查API返回的状态
            if 'ret' in data and data['ret'] == 200:
                # 提取角度数据
                if 'data' in data and 'angle' in data['data']:
                    angle = float(data['data']['angle'])
                    print(f"API响应成功: 角度={angle}°, 状态={data['data'].get('status', 'unknown')}")
                    print(f"缓存状态: {data['data'].get('cached', 'unknown')}, 数据源: {data['data'].get('source', 'unknown')}")
                    return angle
                else:
                    print("API响应格式错误: 缺少angle字段")
                    return None
            else:
                print(f"API返回错误: {data.get('msg', '未知错误')}")
                return None
        else:
            print(f"HTTP请求失败: 状态码 {response.status_code}")
            return None
            
    except requests.exceptions.Timeout:
        print("请求超时")
        return None
    except requests.exceptions.ConnectionError:
        print("连接错误，请检查网络")
        return None
    except json.JSONDecodeError:
        print("JSON解析错误")
        return None
    except Exception as e:
        print(f"未知错误: {str(e)}")
        return None

def main():
    """
    主函数：初始化舵机并开始HTTP轮询控制
    """
    print("初始化MG90S舵机控制...")
    
    # 创建PWM对象，初始化舵机控制
    # 初始位置设为90度（中间位置）
    try:
        servo = pwm.PWM(pwm_id, freq=SERVO_PERIOD, duty=angle_to_duty(90), enable=True)
        print("舵机初始化成功，当前位置: 90度")
        
        # 等待舵机到达初始位置
        time.sleep(1)
        
        print(f"开始HTTP轮询，每{REQUEST_INTERVAL}秒请求一次...")
        print("按Ctrl+C停止程序")
        
        # 用于记录上一次的角度，避免重复设置
        last_angle = 90
        
        while True:
            # 从API获取角度数据
            angle = get_angle_from_api()
            
            if angle is not None:
                # 检查角度是否有变化（避免不必要的舵机动作）
                if abs(angle - last_angle) > 0.5:  # 角度变化超过0.5度才更新
                    # 计算并设置新的PWM占空比
                    duty = angle_to_duty(angle)
                    servo.duty(duty)
                    print(f"舵机角度更新: {last_angle}° -> {angle}° (占空比: {duty:.2f}%)")
                    last_angle = angle
                else:
                    print(f"角度无显著变化: {angle}° (跳过更新)")
            else:
                print("获取角度失败，保持当前位置")
            
            # 等待下次请求
            time.sleep(REQUEST_INTERVAL)
            
    except KeyboardInterrupt:
        print("\n程序被用户中断")
    except Exception as e:
        print(f"程序错误: {str(e)}")
    finally:
        # 清理资源
        try:
            servo.duty(angle_to_duty(90))  # 回到中间位置
            print("舵机回到中间位置")
        except:
            pass
        print("程序结束")

if __name__ == "__main__":
    main()